// 文件名：gpu_memory_transfer.cpp
// 目的：演示将主机端 std::vector<float> 数据上传到“设备内存”的流程。
//      - 若检测到可用的 CUDA 运行时头文件（cuda_runtime.h），将包含并使用真实 CUDA API；
//      - 否则，使用一组模拟（host 上的 malloc/memcpy/free）函数以便在无 CUDA 环境下也能编译/运行。
// 要点：
//   - 条件包含（__has_include）用于在编译期探测 CUDA 头是否可用；
//   - 在模拟环境中，"设备指针" 实际是主机分配的内存，仅用于演示接口与流程；
//   - uploadToDevice 负责分配、拷贝与错误处理（失败时释放并置空指针）。
// 注意：
//   - 真正的 CUDA 环境下，cudaMalloc/cudaMemcpy/cudaFree 作用于 GPU 设备内存；
//   - 向量为空时（size==0）的行为：本示例仍会调用分配 0 字节（不同实现行为可能不同），
//     实际工程中可根据需要特殊处理（例如直接返回成功并置 devicePtr=nullptr）。

#include <vector>
#include <iostream>
#include <cstdlib>
#include <cstring>

#ifdef __has_include
#  if __has_include(<cuda_runtime.h>)
#    include <cuda_runtime.h>  // 真实 CUDA 运行时（若存在）
#    define HAS_REAL_CUDA 1
#  endif
#endif

#ifndef HAS_REAL_CUDA
// -------------------------
// 无 CUDA 环境下的模拟层（Host 上模拟 API）
// 说明：保持与 CUDA 常用 API 的签名兼容，便于在无 CUDA 的机器上演示/单元测试。
// -------------------------
using cudaError_t = int;
constexpr cudaError_t cudaSuccess = 0;
constexpr int cudaMemcpyHostToDevice = 0;

// 模拟设备内存分配：使用 std::malloc
inline cudaError_t cudaMalloc(void** ptr, std::size_t bytes) {
    // 注意：malloc(0) 的返回值由实现定义，可能返回非空指针；此处直接转发。
    *ptr = std::malloc(bytes);
    return (*ptr != nullptr) ? cudaSuccess : 1;
}

// 模拟内存拷贝：使用 std::memcpy
inline cudaError_t cudaMemcpy(void* dst, const void* src, std::size_t bytes, int) {
    std::memcpy(dst, src, bytes);
    return cudaSuccess;
}

// 模拟释放：使用 std::free
inline cudaError_t cudaFree(void* ptr) {
    std::free(ptr);
    return cudaSuccess;
}

// 模拟错误字符串：简单返回固定说明
inline const char* cudaGetErrorString(cudaError_t) {
    return "Simulated CUDA runtime (alloc/memcpy on host)";
}
#endif

// 将主机端数据上传到设备端内存（或模拟设备内存）
// 参数：
//   hostData  —— 主机端连续内存（std::vector<float>）
//   devicePtr —— 输出参数，返回设备端指针地址（成功时非空，失败时置空）
// 返回：CUDA 风格错误码（0 表示成功）
// 行为：
//   1) 计算字节数并分配设备内存；
//   2) 将主机数据拷贝到设备内存；
//   3) 若拷贝失败，释放并置空指针。
cudaError_t uploadToDevice(const std::vector<float>& hostData, void** devicePtr) {
    const std::size_t bytes = hostData.size() * sizeof(float);

    // 分配设备内存
    cudaError_t err = cudaMalloc(devicePtr, bytes);
    if (err != cudaSuccess) return err;

    // 将数据从 Host 复制到 Device
    err = cudaMemcpy(*devicePtr, hostData.data(), bytes, cudaMemcpyHostToDevice);
    if (err != cudaSuccess) {
        // 拷贝失败则清理资源并置空
        cudaFree(*devicePtr);
        *devicePtr = nullptr;
    }
    return err;
}

int main() {
    // 准备主机数据：生成 16 个 [0, 1) 之间的等间距样本
    std::vector<float> samples(16);
    for (std::size_t i = 0; i < samples.size(); ++i) {
        samples[i] = static_cast<float>(i) / static_cast<float>(samples.size());
    }

    // 上传到设备（或模拟设备）内存
    void* devicePtr = nullptr;
    cudaError_t err = uploadToDevice(samples, &devicePtr);

    if (err == cudaSuccess) {
        std::cout << "Upload succeeded. Device memory address: " << devicePtr << '\n';
        // 使用完毕后释放设备内存
        cudaFree(devicePtr);
        // 可选：防止悬空指针
        devicePtr = nullptr;
    } else {
        std::cerr << "Upload failed: " << cudaGetErrorString(err) << '\n';
    }
    return 0;
}